﻿using System;
using System.Collections.Generic;
using System.Threading;
using Windows.Foundation;
using Windows.System.Threading;

namespace SharpDepend.WinRT
{
    internal class ThreadManager : IThreadManager
    {
        class ThreadWinRT : IThread
        {
            private Delegate mAction;
            private ManualResetEvent mJoinEvent;
            private string mName;

            public ThreadWinRT(string name, Delegate action)
            {
                mName = name;
                mAction = action;
            }

            /// <summary>
            /// Reference: https://social.msdn.microsoft.com/forums/windowsapps/en-us/c7ad6866-b26c-436d-9ece-27b6ae90274b/threading-best-practices-in-winrt
            /// </summary>
            public async void Start()
            {
                if (mJoinEvent != null)
                {
                    throw new NotSupportedException("Can not start a thread twice.");
                }
                mJoinEvent = new ManualResetEvent(true);
                await ThreadPool.RunAsync(WorkItemHandler, WorkItemPriority.Normal);
            }

            void WorkItemHandler(IAsyncAction operation)
            {
                mAction.DynamicInvoke();
                mJoinEvent.Set();
            }

            public void Join()
            {
                mJoinEvent.WaitOne();
            }
        }


        private Dictionary<int, ManualResetEvent> mSleepObjectPerThread = new Dictionary<int, ManualResetEvent>();

        public IThread CreateThread(string name, Delegate action)
        {
            return new ThreadWinRT(name, action);
        }

        public void RunThread(string name, Delegate method)
        {
            IThread thread = CreateThread(name, method);
            thread.Start();
        }

        /// <summary>
        /// Reference: http://mikaelkoskinen.net/thread-sleep-in-winrt/
        /// </summary>
        /// <param name="milliseconds">Sleep in milliseconds.</param>
        public void Sleep(int milliseconds)
        {
            ManualResetEvent manual = GetEventResetFromThread();

            manual.WaitOne(milliseconds);
        }

        private ManualResetEvent GetEventResetFromThread()
        {
            int id = Environment.CurrentManagedThreadId;

            if (!mSleepObjectPerThread.ContainsKey(id))
            {
                ManualResetEvent manual = new ManualResetEvent(false);
                lock (mSleepObjectPerThread)
                {
                    mSleepObjectPerThread.Add(id, manual);
                }
                return manual;
            }
            else
            {
                lock (mSleepObjectPerThread)
                {
                    return mSleepObjectPerThread[id];
                }
            }
        }
    }
}
